#include "_public.h"

// 代理路由参数的结构体
struct st_route
{
    int listenport; // 本地监听的通讯端口
    char dstip[31]; // 目标主机的ip地址
    int dstport;    // 目标主机的通讯端口
    int listensock; // 本地监听的socket
} stroute;

CLogFile logfile;
void EXIT(int sig);

vector<struct st_route> vroute;      // 代理路由的容器
bool loadroute(const char *inifile); // 把代理路由参数加载到vroute容器
int conntodst(const char *ip, const int port);

// 初始化服务端的监听端口
int initserver(int port);

#define MAXSOCK 1024
int clientsocks[MAXSOCK]; // 存放每个socket连接对端的socket的值
int clientatime[MAXSOCK]; // 存放每个socket连接最后一次收发报文的时间

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        printf("\n");
        printf("usage: ./inetd logfile inifile\n");
        printf("Sample:./inetd /tmp/inetd.log /etc/inetd.conf\n\n");
        printf("        /project/tools1/bin/procctl 5 /project/tools1/bin/inetd /tmp/inetd.log /etc/inetd.conf\n\n");
        return -1;
    }

    // 关闭全部的信号和输入输出
    CloseIOAndSignal();
    signal(SIGINT, EXIT);
    signal(SIGTERM, EXIT);

    // 打开日志文件
    if (logfile.Open(argv[1], "a+") == false)
    {
        printf("打开日志文件失败(%s)\n", argv[1]);
        return -1;
    }

    // 把代理路由参数加载到vroute容器
    if (loadroute(argv[2]) == false)
    {
        return -1;
    }
    logfile.Write("加载代理路由参数成功(%d)\n", vroute.size());

    // 初始化服务端用于监听的socket
    for (int i = 0; i < vroute.size(); ++i)
    {
        if ((vroute[i].listensock = initserver(vroute[i].listenport)) < 0)
        {
            logfile.Write("initserver(%d) failed.\n", vroute[i].listenport);
            return -1;
        }

        // 把监听socket设置为非阻塞
        fcntl(vroute[i].listensock, F_SETFL, fcntl(vroute[i].listensock, F_GETFD, 0) | O_NONBLOCK);
        // 创建epoll句柄
        int epollfd = epoll_create(1);
        struct epoll_event ev; // 声明事件的数据结构
        // 为监听的socket准备可读事件
        for (int i = 0; i < vroute.size(); ++i)
        {
            ev.events = EPOLLIN;                                          // 读事件
            ev.data.fd = vroute[i].listensock;                            // 指定事件的自定义数据，随着epoll_wait()返回的事件一并返回
            epoll_ctl(epollfd, EPOLL_CTL_ADD, vroute[i].listensock, &ev); // 把监听的socket事件加入epollfd中
        }

        struct epoll_event evs[10]; // 存放epoll返回的事件
        while (true)
        {
            // 等待监视的socket有事件发生
            int infds = epoll_wait(epollfd, evs, 10, -1);

            // selelct调用失败
            if (infds < 0)
            {
                perror("epoll() failed");
                break;
            }

            // 遍历epoll返回已发生事件的数组
            for (int i = 0; i < infds; ++i)
            {
                printf("events=%d, data.fd=%d\n", evs[i].events, evs[i].data.fd);
                // 如果发生事件的是listensock，表示有新的客户端连上来
                int j = 0;
                for (j = 0; j < vroute.size(); ++j)
                {
                    if (evs[i].data.fd == vroute[j].listensock)
                    {
                        // 接收客户端的连接
                        struct sockaddr_in client;
                        socklen_t len = sizeof(client);
                        int srcsock = accept(vroute[i].listensock, (struct sockaddr *)&client, &len);
                        if (srcsock < 0)
                        {
                            // perror("accept() failed");
                            break;
                        }
                        if (srcsock >= MAXSOCK)
                        {
                            logfile.Write("连接数已超过最大值%d\n", MAXSOCK);
                            close(srcsock);
                            break;
                        }
                        logfile.Write("accept client(socket=%d) ok.\n", srcsock);

                        // 向目标ip和端口发起socket连接
                        int dstsock = conntodst(vroute[i].dstip, vroute[i].dstport);
                        if (dstsock < 0)
                        {
                            break;
                        }
                        if (dstsock >= MAXSOCK)
                        {
                            logfile.Write("连接数已超过最大值%d\n", MAXSOCK);
                            close(dstsock);
                            break;
                        }
                        logfile.Write("accept on port %d client(%d, %d) ok.\n", vroute[j].listenport, srcsock, dstsock);

                        // 把新客户端准备两个socket可读事件，并添加到epoll中
                        ev.data.fd = srcsock;
                        ev.events = EPOLLIN;
                        epoll_ctl(epollfd, EPOLL_CTL_ADD, srcsock, &ev);
                        ev.data.fd = dstsock;
                        ev.events = EPOLLIN;
                        epoll_ctl(epollfd, EPOLL_CTL_ADD, dstsock, &ev);

                        // 更新clientsocks数组中两端socket的值和活动时间
                        clientsocks[srcsock] = dstsock;
                        clientsocks[dstsock] = srcsock;
                        clientatime[srcsock] = time(0);
                        clientatime[dstsock] = time(0);

                        break;
                    }
                }

                // 如果j<vroute.size()，表示事件在上面的for循环中已被处理
                if (j < vroute.size())
                {
                    continue;
                }

                // 如果发生事件的是listensock，表示有新的客户端连上来
                char buffer[5000]; // 存放从客户端读取的数据
                int buflen = 0;    // 从socket中读到的数据大小

                memset(buffer, 0, sizeof(buffer));
                if ((buflen = recv(evs[i].data.fd, buffer, sizeof(buffer), 0)) <= 0)
                {
                    // 如果客户端的连接已断开
                    logfile.Write("client(eventfd=%d,%d) disconnected.\n", evs[i].data.fd, clientsocks[evs[i].data.fd]);
                    close(evs[i].data.fd);              // 关闭客户端socket
                    close(clientsocks[evs[i].data.fd]); // 关闭客户端对端的socket
                    clientsocks[clientsocks[evs[i].data.fd]] = 0;
                    clientsocks[evs[i].data.fd] = 0;
                    continue;
                }

                // 如果客户端有报文发过来
                logfile.Write("from %d to %d, %d bytes\n", evs[i].data.fd, clientsocks[evs[i].data.fd], buflen);
                send(clientsocks[evs[i].data.fd], buffer, buflen, 0);
            }
        }
    }
    return 0;
}

// 初始化服务端的监听端口
int initserver(int port)
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        printf("socket() failed.\n");
        return -1;
    }

    // Linux如下
    int opt = 1;
    unsigned int len = sizeof(opt);
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, len);
    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &opt, len);

    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);

    if (bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        printf("bind() failed.\n");
        close(sock);
        return -1;
    }

    if (listen(sock, 5) != 0)
    {
        printf("listen() failed.\n");
        close(sock);
        return -1;
    }

    return sock;
}

// 把代理路由参数加载到vroute容器
bool loadroute(const char *inifile)
{
    CFile File;
    if (File.Open(inifile, "r") == false)
    {
        logfile.Write("打开代理路由参数文件失败\n", inifile);
        return false;
    }

    char strBuffer[256];
    CCmdStr CmdStr;
    while (true)
    {
        memset(strBuffer, 0, sizeof(strBuffer));
        if (File.FFGETS(strBuffer, 200) == false)
        {
            break;
        }

        char *pos = strstr(strBuffer, "#");
        if (pos != 0)
        {
            pos[0] = 0;
        }                                      // 删除说明文字
        DeleteRChar(strBuffer, ' ');           // 删除右边的空格
        UpdateStr(strBuffer, "  ", " ", true); // 把两个空格替换成一个空格
        CmdStr.SplitToCmd(strBuffer, " ");
        if (CmdStr.CmdCount() != 3)
        {
            continue;
        }

        memset(&stroute, 0, sizeof(struct st_route));
        CmdStr.GetValue(0, &stroute.listenport);
        CmdStr.GetValue(1, stroute.dstip);
        CmdStr.GetValue(2, &stroute.dstport);
        vroute.push_back(stroute);
    }

    return true;
}

// 向目标ip和端口发起socket连接
int conntodst(const char *ip, const int port)
{
    // 第1步：创建客户端的socket。
    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        return -1;
    }

    // 第2步：向服务器发起连接请求。
    struct hostent *h;
    if ((h = gethostbyname(ip)) == 0) // 指定服务端的ip地址
    {
        close(sockfd);
        return -1;
    }

    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port); // 指定服务端的通讯端口
    memcpy(&servaddr.sin_addr, h->h_addr, h->h_length);

    // 把socket设置为非阻塞
    fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK);

    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    return sockfd;
}

void EXIT(int sig)
{
    logfile.Write("程序退出，sig=%d\n", sig);

    // 关闭全部监听的socket
    for (int i = 0; i < vroute.size(); ++i)
    {
        close(vroute[i].listensock);
    }
    // 关闭全部客户端socket
    for (int i = 0; i < MAXSOCK; ++i)
    {
        if (clientsocks[i] > 0)
        {
            close(clientsocks[i]);
        }
    }
    exit(0);
}